/***************************************************************************
                          expr.h  -  description
                             -------------------
    begin                : Fri Jun 21 2002
    copyright            : (C) 2002 by Michiel Visser
    email                : 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
 
#ifndef EXPR_H
#define EXPR_H

#include "context.h"
#include <vector>

using namespace Obj;
using namespace Sess;
using namespace std;

namespace Expression
{

#define P(x,l) static const int level##x=l;
	#include"parseunits.h"
#undef P

#define PREPENDA_A(op,array) { array.prepend(op); }
#define PREPENDSTR_A(string,array) PREPENDA_A(NEW_OBJ StringObject(string),array)

#define ADDA(op) { array->prepend(op); array++; }
#define ADDSTR(string) ADDA(NEW_OBJ StringObject(string))

inline Object *Expr::evaluateToCopy() { Object *o=ao(); objectGC.paintPermanent(o,1); evaluateToCopy(o); objectGC.paintPermanent(o,0); return o; } 
inline void Expr::evaluateToPersistCopy(void *space) { evaluateToCopy(space); ((Object *)space)->persist(); }
inline Object *Expr::evaluateToPersistCopy() { Object *o=evaluateToCopy(); o->persist(); return o; } 


#define FUNC_COPY void evaluateToCopy(void *space);
#define FUNC_ORIGINAL Object *evaluateToOriginal();
#define FUNC_VOID void evaluateToVoid();

struct ConstExpr: public Expr
{
	Object::Holder m_val;
	ConstExpr(Object *val) { val->duplicateIn(m_val); m_val->GCPaintMyChildrenPerm(1); }
	ConstExpr() {}
	~ConstExpr() { m_val->GCPaintMyChildrenPerm(0);}
	FUNC_COPY
	void evaluateLiteralToCopy(void *space);
	IFDEBUG(void printTyle() const; )
	void toArray(OList::P array) const;
	int level() const;
};

struct SharedLiteralsScopeExpr: public Expr
{
	EPtr m_child;
	SharedLiteralsScopeExpr(EPtr c, int sharedLiteralCoutn);
	int m_sharedLiteralCount;
	FUNC_COPY
	void toArray(OList::P array) const;
	IFDEBUG(void printTyle() const; )
protected:
	void init();
	void finish();
};

struct SharedLiteralExpr: public Expr
{
	int m_index;
	SharedLiteralExpr(int index): m_index(index) {}
	Object *evaluateLiteralToCopy();
	void toArray(OList::P array) const;
	IFDEBUG(void printTyle() const; )
};

struct SharedLiteralExprAssigned: public SharedLiteralExpr
{
	EPtr a;
	SharedLiteralExprAssigned(int index, EPtr expr): SharedLiteralExpr(index), a(expr) {}
	Object *evaluateLiteralToCopy();
	void toArray(OList::P array) const;
	IFDEBUG(void printTyle() const; )
};

struct FuncExpr: public Expr
{
	void toExprArray(OList::P array) const;
};

#define FUNC_EXPR(x) \
struct x##Expr: public FuncExpr\
{\
  x##Expr(): FuncExpr() {}\
	IFDEBUG(void printTyle() const; )\
  void toArray(OList::P array) const { ADDSTR(#x); } \
  FUNC_COPY\
};
#include "funcs.h"
#undef FUNC_EXPR

template<unsigned int Token, int Level=levelUnary> struct UniExpr: public Expr
{
  EPtr a;
  UniExpr(EPtr o):a(o) {}
	IFDEBUG(void printTyle() const { printf("%s",(char *)token()); a->printTyle(); } )
  void toArray(OList::P array) const
  { ADDSTR(token());
  	ADDA(a->toArrayObject(Level));
  }
	int level() const { return Level; }
	virtual String token() const
	{ char s[5]; int pos=0;
	  for(unsigned int i=Token; i!=0; i>>=8) s[pos++]=(char)i;
	  s[pos]=0;
	  return s;
	}
};

#define MC1(a) ((unsigned int)a)
#define MC2(a,b) (((unsigned int)a)|(b<<8))
#define MC3(a,b,c) (((unsigned int)a)|(b<<8)|(c<<16))
#define MC4(a,b,c,d) (((unsigned int)a)|(b<<8)|(c<<16)|(d<<24))

template<unsigned int Token, int Level=levelUnary> struct UniExprSmall: public UniExpr<Token, Level>
{
  UniExprSmall(EPtr o): UniExpr<Token,Level>(o) {}
};

template<unsigned int Token, int Level, int Right=0> class BinaryExpr: public UniExpr<Token,Level>
{
protected:
	EPtr b;
public:
	BinaryExpr(EPtr na, EPtr nb): UniExpr<Token,Level>(na), b(nb) {}
	IFDEBUG(void printTyle() const { this->a->printTyle(); printf("%s",(char *)this->token()); b->printTyle(); } )
  void toArray(OList::P array) const
  { ADDA(this->a->toArrayObject(Level-Right));
  	ADDSTR(this->token());
		ADDA(b->toArrayObject(Level-(1-Right)));
  }
};

#define DEF_T(name,token,level,type,funcs)\
struct name##Expr: public UniExpr##type<token, level>\
{\
  name##Expr(EPtr o): UniExpr##type<token, level>(o) {}\
  funcs \
};
  
#define DEF_BINEXPR(name,super,funcs,...) \
struct name##Expr: public super<__VA_ARGS__> { name##Expr(EPtr na, EPtr nb): super<__VA_ARGS__>(na,nb) {} \
funcs

#define DEF_E(name,Token,Funcs,AssignFuncs) \
DEF_BINEXPR(name,BinaryExpr,Funcs,Token, level##name) \
}; \
DEF_BINEXPR(name##Assign,BinaryExpr,AssignFuncs,Token, levelAssign) \
  String token() const { String s(BinaryExpr<Token,levelAssign>::token()); s[s.length()-1]='='; return s+" "; } \
};

#define DEF_C(name,Token,Funcs) \
DEF_BINEXPR(name,BinaryExpr,Funcs,Token, level##name) \
}; \

DEF_T(Negate,      MC2('-',' '),levelUnary,,FUNC_COPY)
DEF_T(Inverse,     MC2('!',' '),levelUnary,,FUNC_COPY)
DEF_T(BitNot,      MC2('~',' '),levelUnary,,FUNC_COPY)
DEF_T(Length,      MC2('@',' '),levelUnary,,FUNC_COPY)
DEF_T(Describe,    MC3('.','?',' '),levelModify,,FUNC_COPY)
DEF_T(Type,        MC3('@','@',' '),levelUnary,,FUNC_COPY)
DEF_T(Reference,   MC2('&',' '),levelUnary,Small,FUNC_COPY 	void evaluateLiteralToCopy(void *space);)
DEF_T(DeReference ,MC2('*',' '),levelUnary,Small,FUNC_ORIGINAL FUNC_COPY)
DEF_T(Value,       MC3('*','!',' '),levelUnary,Small,FUNC_COPY FUNC_ORIGINAL)
#undef DEF_T

DEF_E(Prepend,    MC4(' ','+','<',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(InsertAt,   MC4(' ',':','+',' '),FUNC_COPY, FUNC_VOID FUNC_COPY FUNC_ORIGINAL)
DEF_E(RemoveAt,   MC4(' ',':','-',' '),FUNC_COPY,FUNC_COPY )
DEF_E(ShiftLeft,  MC4(' ','<','<',' '),FUNC_COPY,FUNC_COPY )
DEF_E(ShiftRight, MC4(' ','>','>',' '),FUNC_COPY,FUNC_COPY )
DEF_E(BitAnd,     MC3(' ','&',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(BitOr,      MC3(' ','|',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(BitXor,     MC3(' ','^',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(Mul,        MC3(' ','*',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(Div,        MC3(' ','/',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(Mod,        MC3(' ','%',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(Add,        MC3(' ','+',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(Sub,        MC3(' ','-',' '),FUNC_COPY,FUNC_COPY )
DEF_E(And,        MC4(' ','&','&',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_E(Or,         MC4(' ','|','|',' '),FUNC_COPY, FUNC_COPY FUNC_VOID)
DEF_C(FindIndex,  MC3(' ','@',' '    ),FUNC_COPY)
DEF_C(Equal,      MC4(' ','=','=',' '),FUNC_COPY)
DEF_C(Unequal,    MC4(' ','!','=',' '),FUNC_COPY)
DEF_C(Smaller,    MC3(' ','<',' '    ),FUNC_COPY)
DEF_C(SmallerEqual, MC4(' ','<','=',' '),FUNC_COPY)
DEF_C(Greater,      MC3(' ','>',' '    ),FUNC_COPY)
DEF_C(GreaterEqual, MC4(' ','>','=',' '),FUNC_COPY)

DEF_BINEXPR(Assignment,BinaryExpr,FUNC_COPY FUNC_VOID, MC3(' ','=',' '),levelAssign,1)
	Object *execute();
};

class DollarBasicExpr: public Expr
{
protected:
	int m_depth;
public:
  DollarBasicExpr(int d): m_depth(d) {}
  FUNC_ORIGINAL
};

class DollarVarExpr: public DollarBasicExpr
{
  short str;
public:
  DollarVarExpr(short d, short s): DollarBasicExpr(d),str(s) {}
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};
#ifdef TYLE_V2
class DollarVarFirstFromMultipleExpr: public DollarVarExpr
{
public:
  DollarVarFirstFromMultipleExpr(): DollarVarExpr("$") {}
  FUNC_ORIGINAL
};
#if 0
class DollarVarMultiFromSingleExpr: public DollarVarExpr
{
public:
  DollarVarExpr(const char str): DollarVarExpr("$$") {}
  FUNC_ORIGINAL
};
#endif
#endif
class ClosureVarExpr: public Expr
{
	int m_index;
public:
  ClosureVarExpr(int index ): m_index(index) {}
  FUNC_ORIGINAL
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

#undef SUPER
#define SUPER UniExpr<MC2('?',' '),levelModify>
class ClosuredExprExpr: public SUPER
{
	ClosureScope::Ptr	m_scope;
public:
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
  ClosuredExprExpr(EPtr expr, ClosureScope::Ptr scope): SUPER(expr), m_scope(scope) {}
  FUNC_COPY
};

struct TypeSelectCarrier
{
  List<TypeFunc> m_typeFuncs;
  TypeSelectCarrier(List<TypeFunc> &funcs) { m_typeFuncs.take(funcs); }
  void assignTypeVars() const;
};
  
class SingleParamExpr: public DollarBasicExpr
{
  SingleParamScope::Ptr m_scope;
	//IFDEBUG(void printTyle() const; )
public:
  void toArray(OList::P array) const;
  SingleParamExpr(int depth, SingleParamScope::Ptr scope): DollarBasicExpr(depth), m_scope(scope) {}
#ifdef DEBUGLOGGING
  FUNC_ORIGINAL
#endif
	IFDEBUG(void printTyle() const; )
};

class ParamAssignmentExpr: public UniExpr<0,levelParam>, public TypeSelectCarrier
{
protected:
  Scope::Ptr m_scope;
  ParamAssignmentExpr(List<TypeFunc> &funcs, Scope::Ptr scope):
    UniExpr<0,levelParam>(EPtr()), TypeSelectCarrier(funcs), m_scope(scope) {}
};

class SingleParamAssignmentExpr: public ParamAssignmentExpr
{
	void initEval() const;
public:
  Ptr<TypeFunc>::Own m_func;
  FUNC_COPY
  FUNC_VOID
  SingleParamAssignmentExpr(TypeFunc *func, List<TypeFunc> &funcs, SingleParamScope::Ptr scope):
    ParamAssignmentExpr(funcs,scope), m_func(func) {}
  void toArray(OList::P array) const;
	IFDEBUG(void printTyle() const; )
};

class ParamSetAssignmentExpr: public ParamAssignmentExpr
{
	void initEval() const;
public:
  FUNC_COPY
  FUNC_VOID
  void toArray(OList::P array) const;
  ParamSetAssignmentExpr(List<TypeFunc> &funcs, SingleParamScope::Ptr scope):
    ParamAssignmentExpr(funcs,scope) {}
	IFDEBUG(void printTyle() const; )
};

class ParamExpr: public DollarBasicExpr // for multiple parameters
{
  StructIndex m_index;
public:
  ParamExpr(int d, String name): DollarBasicExpr(d),m_index(name) {}
  FUNC_ORIGINAL
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
  bool updateIndex();
};

class BlockVarExpr: public Expr
{
  LocalVarVectorQueue::Ptr queue;
  int number;
public:
  BlockVarExpr(LocalVarVectorQueue::Ptr q, int n): queue(q), number(n) {}
  FUNC_COPY
  FUNC_ORIGINAL
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

class LocalBlockVarExpr: public BlockVarExpr
{
public:
  LocalBlockVarExpr(LocalVarVectorQueue::Ptr q, int n): BlockVarExpr(q,n) {}
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

class GlobalVarInitialExpr: public Expr // compiler assert needed on: sizeof(GlobalVarExpr)==sizeof(GlobalVarInitialExpr)
{
  String m_name;
public:
  GlobalVarInitialExpr(String name): m_name(name) {}
  FUNC_ORIGINAL
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

class GlobalVarExpr: public Expr
{
  GlobalVariable &m_v;
public:
  GlobalVarExpr(GlobalVariable &v): m_v(v) {}
  FUNC_ORIGINAL
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

#undef SUPER
#define SUPER UniExpr<0,levelSwitch>
class SwitchOnExpr: public SUPER
{ // this is nice! (but diffy to understand)
	EList m_choices;
public:
	SwitchOnExpr(EPtr o, EList &choices): SUPER(o) { m_choices.take(choices); }
	FUNC_COPY
	IFDEBUG(void printTyle() const; )
	void toArray(OList::P array) const;
};

struct MapExpr: public Expr
{
	List<EPtr> m_elements;
	typedef EList::P P;
	MapExpr():Expr() {}
	FUNC_COPY
	void evaluateLiteralToCopy(void *space);
	IFDEBUG(void printTyle() const; )
	void toArray(OList::P array) const;
#ifdef VIRTUAL_PERSIST_COPY
	Object *evaluateToPersistCopy();
#endif
};

struct BlockExpr: public Expr
{
	LocalVarVectorQueue::Ptr m_queue;
	BlockExpr():m_queue() {}
	inline void setQueue(LocalVarVectorQueue::Ptr p){	if (!p->isEmpty()) m_queue=p; }
	int level() const;
protected:
	void push() {	if (!!m_queue) { session.execPushLvvq(m_queue);} }
	void pop() { if (!!m_queue) { session.execPopLvvq(); session.m_curTypePtr=0; } }
	~BlockExpr();
};

struct ListExpr: public BlockExpr
{
	struct Elem {
		EPtr elem;
		bool keepResult;
		Elem(EPtr e, bool k): elem(e), keepResult(k) {}
	};
	typedef List<Elem>::P P;
	List<Elem> m_elements;
	ListExpr():BlockExpr() {}
	//  ListExpr(List<Elem> &e,VarVectorQueue &q):BlockExpr(q) { m_elements.take(e); }
	FUNC_COPY
#ifdef VIRTUAL_PERSIST_COPY
	Object *evaluateToPersistCopy();
#endif
	void evaluateLiteralToCopy(void *space);
	IFDEBUG(void printTyle() const; )
	virtual void toArray(OList::P array) const;
};

struct SequenceExpr: public BlockExpr
{
	EList m_elements;
	typedef EList::P P;
	bool keepResult;
	SequenceExpr():BlockExpr() {}
	FUNC_COPY
	IFDEBUG(void printTyle() const; )
	void toArray(OList::P array) const;
};

DEF_BINEXPR(Index,BinaryExpr,FUNC_ORIGINAL,0,levelIndex)
	IFDEBUG(void printTyle() const; )
	void toArray(OList::P array) const;
	FUNC_COPY
};

DEF_BINEXPR(IndexAlt,BinaryExpr,FUNC_COPY,0,levelIndex)
	IFDEBUG(void printTyle() const; )
	void toArray(OList::P array) const;
};

struct TypeSelectionExpr: public UniExpr<0,levelVar>, public TypeSelectCarrier
{ 
	Ptr<TypeFunc>::Own m_func;
	TypeSelectionExpr(TypeFunc *func, List<TypeFunc> &funcs):
	UniExpr<0,levelVar>(EPtr()), TypeSelectCarrier(funcs),m_func(func) {}
	FUNC_ORIGINAL
	FUNC_COPY
	IFDEBUG(void printTyle() const; )
	void toArray(OList::P array) const;
};

struct StructIndexingExpr: public UniExpr<0,levelStructIndex>
{ 
  StructIndex m_index;
  StructIndexingExpr(EPtr na, String name): UniExpr<0,levelStructIndex>(na), m_index(name) {}
  FUNC_ORIGINAL
  FUNC_COPY
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
protected:
  bool updateIndex();
};

struct StructNameExpr: public UniExpr<0,levelStructIndex>
{ 
  StructNameExpr(EPtr na): UniExpr<0,levelStructIndex>(na) {}
  FUNC_COPY
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

struct StructElemsExpr: public UniExpr<0,levelStructIndex> 
{ 
  StructElemsExpr(EPtr na): UniExpr<0,levelStructIndex>(na) {}
  FUNC_COPY
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};


struct EvalExpr: public UniExprSmall<0,0>
{
  EvalExpr(EPtr na): UniExprSmall<0,0>(na) {}
  FUNC_COPY
  FUNC_VOID
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

struct StructIndexingNumberExpr: public StructIndexingExpr
{
  StructIndexingNumberExpr(EPtr na, String name): StructIndexingExpr(na,name) {}
  FUNC_COPY
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

struct StructLengthExpr: public UniExpr<0,levelStructIndex>
{
  StructLengthExpr(EPtr na): UniExpr<0,levelStructIndex>(na) {}
  FUNC_COPY
//	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};


struct HeadStructIndexingExpr: public StructIndexingExpr
{
  HeadStructIndexingExpr(EPtr na, String name): StructIndexingExpr(na,name) {}
  FUNC_COPY FUNC_ORIGINAL
//	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
	bool updateIndex() 
	{ TypeVar s = session.getCurType();
		if (!!s)
		{	session.m_curTypePtr = s->getKeyPtr();
			if (!!session.m_curTypePtr) return StructIndexingExpr::updateIndex();
		}
	  session.reportRuntimeError(String("Indexing of non-struct in '")+toTyle()+"'.");
		return false;
	};
	int level() const;
};

struct HeadCallExpr: public HeadStructIndexingExpr
{
	EPtr b;
  HeadCallExpr(EPtr na, EPtr nb, String name): HeadStructIndexingExpr(na,name), b(nb) {}
  FUNC_COPY
//	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

DEF_BINEXPR(While,BinaryExpr,FUNC_COPY, MC4(' ','?','!',' '),levelSwitch)};

DEF_BINEXPR(ActOn,BinaryExpr,FUNC_COPY, MC4(' ','.','!',' '),levelIterators,1)};

DEF_BINEXPR(ActOnMulti,BinaryExpr,FUNC_COPY, MC4(' ',':','!',' '),levelIterators,1)};

DEF_BINEXPR(ActOnColumns,BinaryExpr,FUNC_COPY, MC4(' ','%','!',' '),levelIterators,1)};

DEF_BINEXPR(IndexQuery,BinaryExpr,FUNC_COPY, MC4(' ',':','@',' '),levelIterators,1)};

DEF_BINEXPR(Apply,BinaryExpr,FUNC_COPY FUNC_VOID, 0,levelVar)
	IFDEBUG(void printTyle() const; )
  void toArray(OList::P array) const;
};

DEF_BINEXPR(ActAbstract,BinaryExpr,FUNC_COPY, MC4(' ','.','?',' '),levelIterators,1)};

} // namespace

#endif // FILEXXX_H
